/**
 * 
 *  Copyright (c) 2008. Aaron Cordova
 *  
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *  
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *  
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *  THE SOFTWARE.
 *  
 */

package edu.umd.cs.cmsc434.terperature;


import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.TreeMap;
import java.util.Map.Entry;
import java.util.Date;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.mortbay.jetty.Server;
import org.mortbay.jetty.handler.HandlerCollection;
import org.mortbay.jetty.handler.ResourceHandler;
import org.mortbay.jetty.servlet.Context;
import org.mortbay.jetty.servlet.ServletHolder;

public class TerpServer {

	public final static String DB_FILE = "terp.db";

	private static class TEntry implements Comparable<TEntry> {

		public String room;
		public long timestamp;
		public int temp;

		public TEntry() {

		}

		public TEntry(TEntry e)  {
			this.room = e.room;
			this.timestamp = e.timestamp;
			this.temp = e.temp;
		}

		public TEntry(String room, long timestamp, int temp) {
			this.room = room;
			this.timestamp = timestamp;
			this.temp = temp;
		}

		public void read(DataInputStream in) throws IOException {
			room = in.readUTF();
			timestamp = in.readLong();
			temp = in.readInt();
		}

		public void write(DataOutputStream out) throws IOException {
			out.writeUTF(room);
			out.writeLong(timestamp);
			out.writeInt(temp);
		}

		//@Override
		public int compareTo(TEntry o) {
			return (int)(this.timestamp - o.timestamp);
		}	


	}

	private static class FKV implements Iterable<Entry<String, List<TEntry>>> {
		private TreeMap<String, List<TEntry>> entries;

		public FKV() {
			entries = new TreeMap<String, List<TEntry>>();
		}

		public FKV(String filename) throws IOException {
			entries = new TreeMap<String, List<TEntry>>();
			load(filename);
		}

		public synchronized void load(String filename) throws IOException  {
			DataInputStream in;
			try {
				in = new DataInputStream(new FileInputStream(filename));
				String building;
				TEntry e = new TEntry();
				int numBuildingEntries;
				LinkedList<TEntry> buildingEntries; 
				while(in.available() > 0) {
					building = in.readUTF();
					numBuildingEntries = in.readInt();
					buildingEntries = new LinkedList<TEntry>();
					for(int i=0; i < numBuildingEntries; i++) {
						e.read(in);
						buildingEntries.add(new TEntry(e));
					}
					entries.put(new String(building), buildingEntries);
				}
				in.close();
			} catch (FileNotFoundException e1) {

			}
		}

		public synchronized void save(String filename) throws IOException {
			DataOutputStream out = new DataOutputStream(new FileOutputStream(filename));
			for(Entry<String, List<TEntry>> e : entries.entrySet()) {
				out.writeUTF(e.getKey());
				out.writeInt(e.getValue().size());
				for(TEntry buildingEntry : e.getValue())
					buildingEntry.write(out);
			}
			out.close();
		}

		public synchronized void put(String building, TEntry entry) {
			if(!entries.containsKey(building))
				entries.put(building, new LinkedList<TEntry>());
			entries.get(building).add(entry);
		}

		public synchronized List<TEntry> get(String building) {
			return entries.get(building);
		}

		//@Override
		public Iterator<Entry<String, List<TEntry>>> iterator() {
			return entries.entrySet().iterator();
		}
	}

	private static class EntryServlet extends HttpServlet {
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		private FKV fkv;

		public EntryServlet(FKV fkv)   {
			this.fkv = fkv;
		}

		/**
		 * This servlet just inserts updates to the database
		 */
		protected void doGet(HttpServletRequest request, HttpServletResponse response)  throws IOException {
			response.setContentType("text/html");
			response.setStatus(HttpServletResponse.SC_OK);
			PrintWriter writer = response.getWriter();

			String building = request.getParameter("building");
			if(building == null) {
				writer.println("entry");
				return;
			}

			String room = request.getParameter("room");
			int temp = Integer.parseInt(request.getParameter("temp"));
			
			String lang = request.getParameter("lang");

			synchronized(fkv) {
				fkv.put(building, new TEntry(room, System.currentTimeMillis(), temp));
			}

			// TODO: differentiate languages
			if(lang != null) {
				writer.println("<html><head><meta HTTP-EQUIV=\"REFRESH\" content=\"0; url=http://127.0.0.1:8888/thankyou_k.html\"></head></html>" );
			}
			else {
				writer.println("<html><head><meta HTTP-EQUIV=\"REFRESH\" content=\"0; url=http://127.0.0.1:8888/thankyou.html\"></head></html>" );
			}
		}
	}

	/**
	 * aggregates the data currently in the db
	 * creates an image and displays it
	 * 
	 * @author aaron
	 *
	 */
	private static class ViewServlet extends HttpServlet {

		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		private FKV fkv;

		public ViewServlet(FKV fkv) {
			this.fkv = fkv;
		}

		protected void doGet(HttpServletRequest request, HttpServletResponse response)  throws IOException {
			response.setContentType("text/html");
			response.setStatus(HttpServletResponse.SC_OK);
			PrintWriter writer = response.getWriter();

			StringBuilder answer = new StringBuilder();

			long startDate = 0L;
			String startDateString = request.getParameter("startdate");
			if(startDateString != null)
				startDate = Long.parseLong(startDateString);
			
			long stopDate = 0L;
			String stopDateString = request.getParameter("stopdate");
			if(stopDateString != null)
				stopDate = Long.parseLong(stopDateString);
			
			//System.out.println(startDate + " " + stopDate);
			
			synchronized(fkv) {
				for(Entry<String, List<TEntry>> entry : fkv) {

					int sum = 0;
					for(TEntry buildingEntry : entry.getValue()) {
						//System.out.print(buildingEntry.timestamp);
						if(startDate == 0L || (buildingEntry.timestamp >= startDate && buildingEntry.timestamp <= stopDate)) {
							sum += buildingEntry.temp;
							//System.out.println(" in");
						}
						else {
							//System.out.println(" out");
						}
					}

					answer.append(entry.getKey() + "=" + sum + "&");
				}
			}

			if(answer.length() > 2) {
				answer.delete(answer.length()-1, answer.length());
				writer.println(answer.toString());
			}
		}
	}

	/**
	 * prepares a CSV file from the database and sends it to the user
	 * 
	 * @author aaron
	 *
	 */
	private static class DataServlet extends HttpServlet {

		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		private FKV fkv;

		public DataServlet(FKV fkv) {
			this.fkv = fkv;
		}

		protected void doGet(HttpServletRequest request, HttpServletResponse response)  throws IOException {
			response.setContentType("text/html");
			response.setStatus(HttpServletResponse.SC_OK);
			PrintWriter writer = response.getWriter();

			// convert database to csv and present to user
			synchronized(fkv) {
				for(Entry<String, List<TEntry>> entry : fkv) {
					String building = entry.getKey();
					for(TEntry buildingEntry : entry.getValue()) {
						writer.println(building + "," 
								+ buildingEntry.room + "," 
								+ (new Date(buildingEntry.timestamp)).toString() + "," 
								+ (buildingEntry.temp>0?"hot":"cold"));
					}
				}
			}
		}
	}


	public static void main(String[] args) throws Exception {
		FKV fkv = new FKV(DB_FILE);

		Server server = new Server(8888);

		HandlerCollection handlers = new HandlerCollection();
		server.setHandler(handlers);

		ResourceHandler resourceHandler = new ResourceHandler();
		resourceHandler.setResourceBase("static/");

		handlers.addHandler(resourceHandler);

		EntryServlet es = new EntryServlet(fkv);
		Context entry = new Context(handlers, "/entry", Context.SESSIONS);
		entry.addServlet(new ServletHolder(es), "/*");

		Context view = new Context(handlers, "/view", Context.SESSIONS);
		view.addServlet(new ServletHolder(new ViewServlet(fkv)), "/*");

		Context data = new Context(handlers, "/data", Context.SESSIONS);
		data.addServlet(new ServletHolder(new DataServlet(fkv)), "/*");

		server.start();

		System.out.println("Hit enter to stop server.");
		BufferedReader bin = new BufferedReader(new InputStreamReader(System.in));
		bin.readLine();

		fkv.save(DB_FILE);
		System.out.println("saved data.");
		server.stop();
	}
}
